//@(#) Output logging stream (using ::syslog(3))
// Author: Andrew Wingorodov <http://andr.ru/>
// Licence: LGPLv3
// QNX4, g++ 2.91.5
// $Id: logostream.h,v 1.1.1.1 2007-12-20 15:53:44 wingorodov Exp $

#ifndef _LOG_OSTREAM_H
#define _LOG_OSTREAM_H 1

#include <syslog.h>
#include <stdarg.h>

#include <streambuf.h>
#include <iosfwd>
#include <string>
#include <memory>

#define BUFSIZE	1024

namespace log {

///\brief Priority of message
enum pri {
	  level_emerg   = LOG_EMERG ///< A panic condition
	, level_alert   = LOG_ALERT ///< A condition that should be corrected
	, level_critical= LOG_CRIT ///< Critical condition, e.g, hard device err
	, level_error   = LOG_ERR ///< Errors
	, level_warning = LOG_WARNING ///< Warning messages
	, level_notice  = LOG_NOTICE ///< Possibly be handled specially
	, level_info    = LOG_INFO ///< Informational
	, level_debug   = LOG_DEBUG ///< For debugging program
};

///\brief Facility of message
enum fac {
	  level_auth    = LOG_AUTH	///< The authorization system
	, level_privat  = LOG_AUTHPRIV ///< The same as auth
	, level_cron    = LOG_CRON ///< The cron daemon
	, level_daemon  = LOG_DAEMON ///< Systems daemons
	, level_ftp     = LOG_FTP ///< The FTP daemon
	, level_kern    = LOG_KERN ///< Messages generated by the kernel
	, level_lpr     = LOG_LPR ///<  The line printer spooling
	, level_mail    = LOG_MAIL ///< The mail system
	, level_news    = LOG_NEWS ///< The network news
	, level_syslog  = LOG_SYSLOG ///< Messages generated internally
	, level_uucp    = LOG_UUCP ///< The uucp system
	, level_local0  = LOG_LOCAL0 ///< Reserved
	, level_local1  = LOG_LOCAL1 ///< Reserved
	, level_local2  = LOG_LOCAL2 ///< Reserved
	, level_local3  = LOG_LOCAL3 ///< Reserved
	, level_local4  = LOG_LOCAL4 ///< Reserved
	, level_local5  = LOG_LOCAL5 ///< Reserved
	, level_local6  = LOG_LOCAL6 ///< Reserved
	, level_local7  = LOG_LOCAL7 ///< Reserved
};

///\brief Logging options
enum opt {
	  logopt_cons    = LOG_CONS ///< Write message to /dev/console
	, logopt_nodelay = LOG_NDELAY///< Open the connection to syslogd immediately
	, logopt_perror  = LOG_PERROR ///< Write to standard error output
	, logopt_pid     = LOG_PID ///< Log the proccess id with each message
};

///\class log_ostrmbuf
///\brief Log stream buffer
class log_ostrmbuf
	: public std::streambuf
{
typedef int int_type;
typedef std::streampos pos_type;

public:
	///\brief Constructor by default
	log_ostrmbuf ()
		: _is_open (false)
	{
		obuffer = (std::auto_ptr<char>) new char [BUFSIZE] ;
	}

	///\brief Constructor
	log_ostrmbuf (
		  const char* ident_ ///\param ident_ Identification of log
		, const opt logopt_   ///\param logopt_ Log options
		, const fac facility_ ///\param facility_ Log facility
	)
		: _is_open (false)
	{
		obuffer = (std::auto_ptr<char>) new char [BUFSIZE] ;
		open (ident_, logopt_, facility_);
	}

	///\brief Destructor
	~log_ostrmbuf () { close (); }
//@}

	///\brief Allocation memory and initialization buffer.
	void open (
		  const char* ident_  ///\param ident_ Identification for openlog(3)
		, const opt logopt_   ///\param logopt_ Log options
		, const fac facility_ ///\param facility_ Log facility
	) {
		if (is_open()) close();

		::openlog (ident_, logopt_, facility_);
		this->setp (obuffer.get(), obuffer.get()+BUFSIZE-1);

		_is_open = true ;
	}

	///\brief Close buffer
	void close ()
	{
		if ( !is_open() ) return;
		sync ();
		this->setp (0,0);
		::closelog ();
		_is_open = false;
	}

	///\brief Is open
	///\return True if it is open
	bool is_open () const { return _is_open; }
//@}

protected:
///\name Base IO
//@{
	///\brief Sync output
	///\return non-zero if error 
	virtual int_type sync ();

	///\brief Overflow output
	///\return Last character of EOF
	virtual int_type overflow (
		  int_type c ///\param c Last character
	);
//@}

private:
	log_ostrmbuf (const log_ostrmbuf&);
	log_ostrmbuf& operator=(const log_ostrmbuf&);

	std::auto_ptr<char> obuffer;
	bool _is_open;

}; //.log_ostrmbuf


///\class logios 
///\brief Base IOS for log::ostream
class logios
	: virtual public std::ios
{
protected:
	///\brief Constructor by default
	logios ()
		: std::ios (0)
		, obuf()
	{
		this->init ( std::cout.rdbuf ()); }

	///\brief Open to syslogd(3)
	explicit logios (
		  const char* ident_ ///\param ident_ Identification for ::openlog(3)
		, const opt logopt_ ///\param logopt_ Log options
		, const fac facility_ ///\param Log facility
	)
		: std::ios (0)
		, obuf (ident_, logopt_, facility_)
	{}

	///\brief Destructor
	virtual ~logios () { this->close(); }

	log_ostrmbuf obuf; ///< Stream buffer

public:
	///\brief Open log::ostream buffer
	void open (
		  const char* ident_ ///\param ident_ Identification for openlog(3)
		, const opt logopt_ ///\param logopt_ Log options
		, const fac facility_ ///\param Log facility
	) {
		obuf.open (ident_, logopt_, facility_);
		this->init (&obuf);
	}

	///\brief Close log::ostream buffer
	void close ()
	{
		obuf.close ();
		this->init ( std::cout.rdbuf());
	}

	///\brief Is open
	///\return True if is open

	bool is_open () const { return obuf.is_open (); }

	///\brief Get stream buffer
	///\return Pointer to the buffer

	log_ostrmbuf* rdbuf () { return &obuf; }

}; //.class logios

///\class log_state 
///\brief Priority and level state of messages
struct log_state {
	pri priority;
	pri current;

	fac facility;
	opt logopt;

	///\brief Constructor by default
	log_state (const opt opt_ = logopt_cons
					, const fac fac_ = level_local0 )
		: priority (level_error)
		, current (level_error)
		, logopt (opt_)
		, facility (fac_)
	{}

}; //.class log_state

///\class logostream 
///\brief Log output stream
class logostream
	: public std::ostream
	, public logios
{
private:
	typedef std::ostream ostream_type;
	typedef logios base_ios;

	using base_ios::obuf;
	log_state _state;

public:
	///\brief Constructor by default
	logostream () {}

	///\brief Open to the syslogd
	explicit
	logostream (
		  const char* ident_ ///\param ident_ Identification to openlog(3)
		, const opt logopt_ = ///\param logopt_ Log options
						(opt)( logopt_cons | logopt_nodelay | logopt_pid )
		, const fac facility_ = level_local0 ///\param Log facility
	)
		: ostream_type(0)
		, _state (logopt_, facility_)
		, base_ios (ident_, logopt_, facility_)
	{}

///\name Set level and options
//@{
	///\brief Set level of state
	logostream& level (
		pri pri_ ///\param pri_ Output level priority
	) {
		_state.current = pri_;
		return *this;
	}

	///\brief Set level of state
	logostream& logopt (
		opt opt_ ///\param opt_ syslog() option
	) {
		_state.logopt = opt_;
		return *this;
	}
//@}

///\name Manipulators of priority
//@{
	///\brief Manipulator
	///\return Stream reference for cascade
	logostream& operator<< (
			logostream& (*function) (logostream&)///\param function , The
	) {
		return function (*this);
	}

	///\brief Level info
	logostream& info (logostream& out_) { return out_.info(); }

	///\brief Set priority to info
	logostream& info () { _state.priority= level_info; return *this; }

	///\brief Level critical
	logostream& critical (logostream& out_) { return out_.info(); }

	///\brief Set priority to critical
	logostream& critical () { _state.priority= level_critical; return *this; }
//@}

	///\brief IO template
	template <class Type>
	logostream& operator<< (
			  class Type data_///\param data_ Data for output
	) {
		if ( _state.current >= _state.priority ) {
			std::ostream::operator<< (data_);
		}
		return *this;
	}

	///\brief Output for std::string implemantation
	///\return This
	logostream& operator<< (
			 const std::string& str_ ///\param str_ String to output
	) {
		if ( _state.current >= _state.priority ) {
			std::ostream::write (str_.data(), str_.size());
		}
		return *this;
	}

}; //.class logostream

///\name Outside level manipulators
//@{
inline logostream& info (logostream& out_) { return out_.info(); }
inline logostream& critical (logostream& out_) { return out_.critical(); }
//@}

///\name Manipulator with arguments implementation
//@{

///\class argmanip 
///\brief Manipulator with arguments

template <class Type>
struct argmanip {

	///\brief Callback function
	///\param func_ This is it

	logostream& (*func) (logostream&, Type);
	Type arg; ///< Argument of manipulator

	///\brief Constructor
	argmanip (logostream& (*f) (logostream&, Type), Type arg_)
		: func(f)
		, arg (arg_)
	{}
}; //.class argmanip

/**************
///\brief Output manipulator with argument
template <class Type>
inline
logostream& operator<< (
	  logostream& outs_ ///\param outs_ Output stream
	, const argmanip<Type>& manip_ ///\param manip_ Manipulator with args
) {
	return manip_.func (outs_, manip_.arg);
}
//@}

///\name Manupulator of level
//@{
///\brief Prefix call to level
inline
logostream& set_level (
	  logostream& outs_ ///\param bios Base IO stream
	, pri arg ///\param arg Argument of manipulator
) {
	return outs_.level (arg);
}

///\brief Level object-function
inline argmanip<pri> level (pri arg) { return argmanip<pri> (set_level, arg); }
//@}

///\name Manupulator of log options
//@{
///\brief Prefix call to logopt
inline
logostream& set_logopt (
	  logostream& outs_ ///\param outs_ IO stream
	, opt arg ///\param arg Argument of manipulator
) {
	return outs_.logopt (arg);
}

///\brief Logopt object-function
inline argmanip<opt> logopt (opt arg) { return argmanip<opt>(set_logopt,arg); }
//@}
****************/


extern logostream s; ///< Global logging stream
} //::log

#endif ///_LOG_OSTREAM_H
